package com.power.powercash;

import com.db4o.ObjectContainer;
import com.db4o.query.Predicate;
import com.power.utils.Date;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author f6036477
 */
public class Consulta <T>
{

  private ObjectContainer db;

  private List<T> result;

  private List<T> sub;

  private Map<Predicate<?>, String> ands;

  private List<Predicate<?>> ors;

  private Predicate<?> first;

  private Class<?> cls;


  public <T> Consulta(Class<T> c)
  {
    db = null;
    cls = c;
    result = new <T> ArrayList();
    sub = new <T> ArrayList();
    ands = new <Predicate<T>, String> HashMap();
    ors = new <Predicate<T>> ArrayList();
    first = null;
  }


  public <T> Consulta(Class<T> c, ObjectContainer db)
  {
    this(c);
    this.db = db;
  }


  public <T> Consulta(Class<T> c, ObjectContainer db, Predicate<T> predicate)
  {
    this(c, db);
    first = predicate;
  }


  public <T> void and(Predicate<T> p, String getMethodName)
  {
    if(p == null || getMethodName == null)
      return;
    ands.put(p, getMethodName);
  }


  public <T> void or(Predicate<T> p)
  {
    if(p == null)
      return;
    ors.add(p);
  }


  public Predicate getPredicate()
  {
    return first;
  }


  public <T> void setPredicate(Predicate<T> p)
  {
    first = p;
  }


  public ObjectContainer getObjectContainer()
  {
    return db;
  }


  public void setObjectContainer(ObjectContainer db)
  {
    this.db = db;
  }


  public String getAndMethodName(Predicate p)
  {
    return ands.get(p);
  }


  public Predicate getAnd(int index)
  {
    if(index < 0 || index > ands.size())
      return null;
    return (Predicate) ands.keySet().toArray()[index];
  }


  public boolean containsAnd(Predicate p)
  {
    return ands.containsKey(p);
  }


  public boolean containsAnd(String getMethodName)
  {
    return ands.containsValue(getMethodName);
  }


  public String removeAnd(Predicate p)
  {
    return ands.remove(p);
  }


  public int getOrCount()
  {
    return ors.size();
  }


  public int getAndCount()
  {
    return ands.size();
  }


  public Predicate<T> getOr(int index)
  {
    if(index < 0 || index > ors.size())
      return null;
    return (Predicate<T>) ors.get(index);
  }


  public boolean containsOr(Predicate p)
  {
    return ors.contains(p);
  }


  public boolean removeOr(Predicate p)
  {
    return ors.remove(p);
  }


  public List<T> getResult()
  {
    return result;
  }


  public void setResult(List r)
  {
    result = r;
  }


  public void clearResult()
  {
    result.clear();
  }


  public void clearAnds()
  {
    ands.clear();
  }


  public void clearOrs()
  {
    ors.clear();
  }


  public List search()
  {
    this.searchFirst();
    this.searchOrs();
    if(result.isEmpty()) return result;
    this.searchAnds();
    return result;
  }


  private void searchOrs()
  {
    if(ors.isEmpty())
      return;

    for(int i = 0; i < ors.size(); i++) {
      List<T> l = (List<T>) db.query(ors.get(i));
      result.addAll(l);
    }//for
  }


  private void searchFirst()
  {
    result = (List<T>) db.query(first);
    System.out.println("searchFirst: "+ result.size());
  }


  private <S> void search(Predicate<S> p, String methodName, int index)
  {
    Object o = result.get(index);

    try {
      Method m = o.getClass().getMethod(methodName);
      //System.out.print("search( p, " + methodName + ", " + index + " )");
      Object c = m.invoke(o);
      //System.out.println(c.toString());
      S s = (S) c;
      //System.out.print("p.match(t): ");
      if(p.match(s)) {
        //System.out.println("true");
        //System.out.println("sub.add(c);");
        sub.add((T) o);
      } //else
        //System.out.println("false");
    } catch(Exception ex) {
      ex.printStackTrace();
    }
  }


  private void searchAnds()
  {
    if(ands.isEmpty() || result.isEmpty())
      return;

    for(int i = 0; i < ands.size(); i++) {
      Predicate p = this.getAnd(i);
      String method = this.getAndMethodName(p);
      for(int j = 0; j < result.size(); j++)
        this.search(p, method, j);
      result = sub;
      sub = new <T> ArrayList();
    }//for
  }


  public static void main(String[] args)
  {
    File f = new File("consulta.db");
    if(f.exists()) f.delete();

    Configuration conf = new Configuration();
    conf.setDBFile(f.getPath());
    Consulta cons = new Consulta(Pagamento.class, conf.startDB());

    Dinheiro d = null;
    Date d1 = new Date();
    Date d2 = Date.parseDate("01.08.2010");
    d1.setFullComparationMode(false);
    d2.setFullComparationMode(false);
    Banco b1 = new Banco("BB", 1, 1);
    Banco b2 = new Banco("Bradesco", 1, 1);
    d = new Dinheiro(50);
    d.setDataEmissao(d1);
    d.setBanco(b1);

    System.out.println("add: 10 x 50 = "+ d.toString());
    System.out.println(d1);
    for(int i = 0; i < 10; i++)
      cons.getObjectContainer().store(d.clone());

    d.setDataEmissao(d2);
    System.out.println("add: 10 x 50 = "+ d.toString());
    System.out.println(d2);
    for(int i = 0; i < 10; i++)
      cons.getObjectContainer().store(d.clone());

    d.setBanco(b2);
    d.setDataEmissao(d2);

    System.out.println("add: 10 x 50 = "+ d.toString());
    System.out.println(d2);
    for(int i = 0; i < 10; i++)
      cons.getObjectContainer().store(d.clone());

    System.out.println("Search:");
    System.out.println("ConsultaPagamento cp = new ConsultaPagamento();");
    ConsultaPagamento cp = new ConsultaPagamento();
    System.out.println("ConsultaDate cd = new ConsultaDate();");
    ConsultaDate cd = new ConsultaDate();

    Pagamento p = new AbstractPagamento() {};
    Banco b3 = new Banco();
    b3.setName("BB");
    Date d3 = Date.parseDate("02.08.2010");
    p.setBanco(b3);

    System.out.println(b3);
    System.out.println(d3);
    System.out.println("cd.setCompareDateMethod(ConsultaDate.COMPARE_AFTER);");

    cp.setPagamento(p);
    cd.setCompareDateMethod(ConsultaDate.COMPARE_AFTER);
    cd.setDate(d3);

    cons.setPredicate(cp);
    cons.and(cd, "getDataEmissao");

    System.out.println("cons.setPredicate(cp);");
    System.out.println("cons.and(cd, getDataEmissao);");

    List l = cons.search();
    
    System.out.println("cons.search();");
    System.out.println("result: "+ l.size());
    for(Object o : l)
      System.out.println("- "+ o.toString());
  }

}
